#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "imp/imp_log.h"

#include "profiles/profiles_management.h"
#include <webrtc_audio_processing/audio_processing.h>
#include <webrtc_audio_processing/audio_buffer.h>
#include <webrtc_audio_processing/module_common_types.h>

#include "webrtc_aec.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define CONFIG_FILE 1
char profile_buf[101] = "";
#define WEBRTC_AEC_TAG "WEBRTC_AEC"

using webrtc::AudioFrame;
using webrtc::AudioBuffer;
using webrtc::AudioProcessing;
using webrtc::EchoCancellation;
using webrtc::EchoControlMobile;
using webrtc::GainControl;
using webrtc::HighPassFilter;
using webrtc::LevelEstimator;
using webrtc::NoiseSuppression;
//using webrtc::VoiceDetection;
using webrtc::ProcessingConfig;

#define AEC		/* acoustic echo cancellation */
//#define AECM	/* acoustic echo control for mobile ;it is not recommended to use*/
#define AGC		/* automatic gain control */
#define HP		/* High Pass Filter */
//#define LE		/* Level Estimator */
#define NS		/* noise suppression */
//#define VAD		/* voice activity detection */

AudioProcessing *apm = NULL;
AudioFrame *far_frame = NULL;
AudioFrame *near_frame = NULL;
ProcessingConfig *processing_config;

static int delay_time = 150;
static float frame_far_v = 1.0;
static float frame_near_v = 1.0;
static int agc_start_volumn = 85;

static int SAMPLE_RATE = 8000;
static int CHANNEL_NUM = 1;
static int SAMPLE_TIME = 10;

#define AGC_STARTUP_MIN_VOLUMN 85
#define AGC_MAX_VOLUMN 255
#if 1
void dump()
{
	int ret;
	int sample_rate_hz_dump = apm->proc_sample_rate_hz();
	int num_input_channels_dump = apm->num_input_channels();
	int num_output_channels_dump = apm->num_output_channels();
	int num_reverse_channels_dump = apm->num_reverse_channels();
	int stream_delay_ms_dump = apm->stream_delay_ms();
	printf("sample rate : %d\n", sample_rate_hz_dump);
	printf("num_input_channels : %d\n", num_input_channels_dump);
	printf("num_output_channels : %d\n", num_output_channels_dump);
	printf("num_reverse_channels : %d\n", num_reverse_channels_dump);
	printf("stream_delay_ms : %d\n", stream_delay_ms_dump);
	printf("Ingenic Aec Version 1.1.0_20161014\n");
/* AEC */
	ret = apm->echo_cancellation()->is_enabled();
	if(ret) {
		printf("AEC enable !\n");
		ret = apm->echo_cancellation()->is_drift_compensation_enabled();
		if(ret) {
			printf("\t\tenable_drift_compensation\n");

			//ret = apm->echo_cancellation()->device_sample_rate_hz();
			//printf("\t\t\tdevice_sample_rate_hz : %d\n", ret);
			ret = apm->echo_cancellation()->stream_drift_samples();
			printf("\t\t\tstream_drift_samples : %d\n", ret);
		}

		ret = apm->echo_cancellation()->suppression_level();
		printf("\t\tsuppression_level : %d\n", ret);

		ret = apm->echo_cancellation()->are_metrics_enabled();
		if(ret) {
			printf("\t\tenable_metrics\n");

			EchoCancellation::Metrics* metrics = NULL;
			apm->echo_cancellation()->GetMetrics(metrics);//dai yong
			if(metrics)
			  printf("echo_return_loss: [%d, %d, %d, %d]\n",metrics->echo_return_loss.instant, metrics->echo_return_loss.average,metrics->echo_return_loss.maximum,metrics->echo_return_loss.minimum);
		}

		ret = apm->echo_cancellation()->is_delay_logging_enabled();
		if(ret) {
			printf("\t\tenable_delay_logging\n");

		//???	apm->echo_cancellation()->GetDelayMetrics();//dai yong
		}

	}
#ifdef AECM
/* AECM */
	ret = apm->echo_control_mobile()->is_enabled();
	if(ret) {
		printf("AECM enable !\n");

		ret = apm->echo_control_mobile()->routing_mode();
		printf("\t\trouting_mode : %d\n", ret);
		printf("\t\tps: 0: kQuietEarpieceOrHeadset 1: kEarpiece 2: kLoudEarpiece 3: kSpeakerphone  4: kLoudSpeakerphone\n");

		ret = apm->echo_control_mobile()->is_comfort_noise_enabled();
		if(ret)
			printf("\t\tenable_comfort_noise\n");

	//???	apm->echo_control_mobile()->GetEchoPath();
	//???	apm->echo_control_mobile()->echo_path_size_bytes();
	}
#endif
#ifdef AGC
/* AGC */
	ret = apm->gain_control()->is_enabled();
	if(ret == 1) {
		printf("AGC enabled !\n");

		ret = apm->gain_control()->mode();
		printf("\t\tmode : %d\n", ret);
		printf("\t\tps: 0: kAdaptiveAnalog 1: kAdaptiveDigital 2: kFixedDigital\n");

		ret = apm->gain_control()->target_level_dbfs();
		printf("\t\ttarget_level_dbfs : %d\n", ret);

		ret = apm->gain_control()->compression_gain_db();
		printf("\t\tcompression_gain_db : %d\n", ret);

		ret = apm->gain_control()->is_limiter_enabled();
		if(ret)
			printf("\t\tlimiter_enabled\n");
	}
#endif
/* HP */
	ret = apm->high_pass_filter()->is_enabled();
	if(ret)
		printf("HighPassFilter is enabled\n");

/* LE */
	ret = apm->level_estimator()->is_enabled();
	if(ret) {
		printf("LevelEstimator is enable\n");
		/* not support */
		/*
		Metrics metrics, reverse_metrics;
		apm->level_estimator()->GetMetrics((Metrics*) &metrics, (Metrics*) &reverse_metrics);
		*/
	}

/* NS */
	ret = apm->noise_suppression()->is_enabled();
	if(ret) {
		printf("NoiseSuppression is enabled !\n");
		ret = apm->noise_suppression()->level();
		printf("\t\tNoiseSuppression : %d\n", ret);
	}
#ifdef VAD
/* VAD */
	ret = apm->voice_detection()->is_enabled();
	if(ret) {
		printf("voice activity detection is enable !\n");

		ret = apm->voice_detection()->likelihood();
		printf("\t\tlikelihood : %d\n", ret);

		ret = apm->voice_detection()->frame_size_ms();
		printf("\t\tframe size per ms : %d\n", ret);
	}
#endif
}
#endif

int ingenic_apm_init(int sample_rate, char *profile_path) {
	SAMPLE_RATE = sample_rate;

	//int32_t sample_rate_hz = SAMPLE_RATE;
	//int num_capture_input_channels = CHANNEL_NUM;
	//int num_capture_output_channels = CHANNEL_NUM;
	//int num_render_channels = CHANNEL_NUM;

	int input_sample_rate_hz = SAMPLE_RATE;
	int output_sample_rate_hz = SAMPLE_RATE;
	int reverse_sample_rate_hz = SAMPLE_RATE;
//	int input_channels = CHANNEL_NUM;
//	int output_channels = CHANNEL_NUM;
//	int reverse_channels = CHANNEL_NUM;

	AudioProcessing::ChannelLayout input_layout = AudioProcessing::kMono;
	AudioProcessing::ChannelLayout output_layout = AudioProcessing::kMono;
	AudioProcessing::ChannelLayout reverse_layout = AudioProcessing::kMono;

	if(apm != NULL){
		printf("AEC init again\n");
		return -1;
	}

	if(far_frame != NULL){
		printf("AEC init again\n");
		return -1;
	}

	if(near_frame != NULL){
		printf("AEC init again\n");
		return -1;
	}

	if(agc_start_volumn > AGC_MAX_VOLUMN) {
	  printf("AGC start volume must not exceed %u", AGC_MAX_VOLUMN);
	  return -1;
	}
	webrtc::Config config;
	config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(false));
	config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(false));
	config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(false,agc_start_volumn));
	config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(false));
	//config.Set<webrtc::Beamforming>(new webrtc::Beamforming(false));
	config.Set<webrtc::Intelligibility>(new webrtc::Intelligibility(false));
/*
	if(config.Get<webrtc::DelayAgnostic>().enabled){
		printf("Unknown Delay\n");
		return -1;
			}
	else
		printf("Fixed Delay\n");
*/
	apm = AudioProcessing::Create(config);
	if(apm == NULL) {
		printf("AudioProcessing::Create() error !\n");
		return -1;
	}

//	webrtc::ProcessingConfig pconfig = {{
//		webrtc::StreamConfig(input_sample_rate_hz, input_channels, false), /* input stream */
//		webrtc::StreamConfig(output_sample_rate_hz, output_channels, false), /* output stream */
//		webrtc::StreamConfig(reverse_sample_rate_hz, reverse_channels, false), /* reverse input stream */
//		webrtc::StreamConfig(reverse_sample_rate_hz, reverse_channels, false), /* reverse output stream */
//	}};
	apm->Initialize(input_sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz, input_layout, output_layout, reverse_layout);
	// if (apm->Initialize(pconfig) != webrtc::AudioProcessing::kNoError) {
	// 	printf("Error initialising audio processing module\n");
	// 	return -1;
	// }

	//apm->set_stream_key_pressed(true);
	//apm->set_delay_offset_ms(0);
/*HP*/
#ifdef HP
	if(read_profile(profile_path, "HP","HP_enable", profile_buf) < 0) {
		apm->high_pass_filter()->Enable(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->high_pass_filter()->Enable(true);
	} else {
		apm->high_pass_filter()->Enable(false);
	}
#endif
#ifdef AEC
/*AEC*/
#if CONFIG_FILE
	if(read_profile(profile_path, "AEC","AEC_enable", profile_buf) < 0) {
		apm->echo_cancellation()->Enable(true); // default value
	} else if(!strcmp(profile_buf, "true")) {
		apm->echo_cancellation()->Enable(true);
	} else {
		apm->echo_cancellation()->Enable(false);
	}

	char* aec_version = apm->echo_cancellation()->get_aec_version();

	if(read_profile(profile_path, "AEC","aec_version", profile_buf) < 0) {
	    printf("unknown aec version in webrtc_profile.ini\n");
	}else if(strncasecmp(profile_buf, aec_version, 5) == 0){
	    printf("aec version: %s in webrtc_profile.ini and %s in aec lib.\n",profile_buf, aec_version);
	}else{
	    printf("unconformity aec version: %s in webrtc_profile.ini but %s in aec lib\n",profile_buf, aec_version);
	}
//	apm->echo_cancellation()->set_device_sample_rate_hz(sample_rate_hz);
//	apm->echo_cancellation()->set_stream_drift_samples(0);

	if(read_profile(profile_path, "AEC","enable_drift_compensation", profile_buf) < 0) {
		apm->echo_cancellation()->enable_drift_compensation(false);
	} else if(!strcmp(profile_buf, "true")) {
	        apm->echo_cancellation()->enable_drift_compensation(true);
	} else {
		apm->echo_cancellation()->enable_drift_compensation(false);
	}
	if(apm->echo_cancellation()->is_drift_compensation_enabled())
	apm->echo_cancellation()->set_stream_drift_samples(0);


	if(read_profile(profile_path, "AEC","set_suppression_level", profile_buf) < 0) {
		apm->echo_cancellation()->set_suppression_level(EchoCancellation::kModerateSuppression);//1
	} else if(!strcmp(profile_buf, "kLowSuppression")) {
		apm->echo_cancellation()->set_suppression_level(EchoCancellation::kLowSuppression);	//0
	} else if(!strcmp(profile_buf, "kModerateSuppression")) {
		apm->echo_cancellation()->set_suppression_level(EchoCancellation::kModerateSuppression);//1
	} else if(!strcmp(profile_buf, "kHighSuppression")) {
		apm->echo_cancellation()->set_suppression_level(EchoCancellation::kHighSuppression);	//2
	} else if(!strcmp(profile_buf, "kMoreSuppression")) {
		apm->echo_cancellation()->set_suppression_level(EchoCancellation::kMoreSuppression);	//3
	}else if(!strcmp(profile_buf, "kMostSuppression")) {
		apm->echo_cancellation()->set_suppression_level(EchoCancellation::kMostSuppression);	//4
	}

   //set_aec_mode
	if(read_profile(profile_path, "AEC","aec_mode", profile_buf) >= 0) {
		if(!strcmp(profile_buf, "user")) {
			apm->echo_cancellation()->set_aec_mode(0);
		} else if(!strcmp(profile_buf, "fvd_debug")) {
			apm->echo_cancellation()->set_aec_mode(1);
		} else if(!strcmp(profile_buf, "dtd_debug")) {
			apm->echo_cancellation()->set_aec_mode(2);
		} else if(!strcmp(profile_buf, "can_debug")) {
			apm->echo_cancellation()->set_aec_mode(3);
		} else if(!strcmp(profile_buf, "sup_debug")) {
			apm->echo_cancellation()->set_aec_mode(4);
		} else if(!strcmp(profile_buf, "mu_debug")) {
			apm->echo_cancellation()->set_aec_mode(5);
		}
	}
#if 0
   //set_dtd_para
	if(read_profile(PROFILE_NAME, "AEC","enable_ifun", profile_buf) < 0) {
	// if(read_profile(PROFILE_NAME, "AEC","enable_dtd", profile_buf) < 0) {
		apm->echo_cancellation()->set_dtd_enable(0);//1
	} else {
		if(!strcmp(profile_buf, "true")) {
			int band_num = 0;
			float pow_thd = 0.0f;
			float dx_ratio = 0.0f;
			if(read_profile(PROFILE_NAME, "AEC","set_ifun_arg1", profile_buf) >= 0)
			// if(read_profile(PROFILE_NAME, "AEC","set_dtd_band_num", profile_buf) >= 0)
				band_num = atoi(profile_buf);
			if(read_profile(PROFILE_NAME, "AEC","set_ifun_arg2", profile_buf) >= 0)
			// if(read_profile(PROFILE_NAME, "AEC","set_dtd_pow_thd", profile_buf) >= 0)
				pow_thd = atof(profile_buf);
			if(read_profile(PROFILE_NAME, "AEC","set_ifun_arg3", profile_buf) >= 0)
			// if(read_profile(PROFILE_NAME, "AEC","set_dtd_dx_ratio", profile_buf) >= 0)
				dx_ratio = atof(profile_buf);
			if(band_num > 0 && pow_thd > 0 && dx_ratio > 0){
				apm->echo_cancellation()->set_dtd_enable(1);//1
				apm->echo_cancellation()->set_dtd_parameters(band_num, dx_ratio, pow_thd);//1
			} else {
				apm->echo_cancellation()->set_dtd_enable(0);//1
			}
		} else {
			apm->echo_cancellation()->set_dtd_enable(0);//1
		}
	}
#endif
	// set suppression mode
	int suppression_mode = 0;
	if(read_profile(profile_path, "AEC","set_suppression_mode", profile_buf) >= 0)
	    suppression_mode = atoi(profile_buf);
	apm->echo_cancellation()->set_suppression_mode(suppression_mode);

	// set far_pow_thd
	float far_pow_thd = 75000.0f;
	if(read_profile(profile_path, "AEC","set_far_pow_thd", profile_buf) >= 0)
	    far_pow_thd = atof(profile_buf);
	apm->echo_cancellation()->set_far_pow_thd(far_pow_thd);

	//set mu value
	float mu_min = 0.0f;
	float mu_decay = 0.0f;
	if(read_profile(profile_path, "AEC","set_mu_min", profile_buf) >= 0)
	    mu_min = atof(profile_buf);
	if(read_profile(profile_path, "AEC","set_mu_decay", profile_buf) >= 0)
	    mu_decay = atof(profile_buf);
	if(mu_min > 0.0 && mu_min < 1.0 && mu_decay > 0.0 && mu_decay < 1.0)
	    apm->echo_cancellation()->set_mu_parameters(mu_min, mu_decay);

	// set cor thd value
	float cor_thd1 =0.0f;
	float cor_thd2 =0.0f;
	float cor_thd3 =0.0f;
	float cor_thd4 =0.0f;
	if(read_profile(profile_path, "AEC","set_cor_thd1", profile_buf) >= 0)
	    cor_thd1 = atof(profile_buf);
	if(read_profile(profile_path, "AEC","set_cor_thd2", profile_buf) >= 0)
	    cor_thd2 = atof(profile_buf);
	if(read_profile(profile_path, "AEC","set_cor_thd3", profile_buf) >= 0)
	    cor_thd3 = atof(profile_buf);
	if(read_profile(profile_path, "AEC","set_cor_thd4", profile_buf) >= 0)
	    cor_thd4 = atof(profile_buf);
	if(cor_thd1 > 0.0 && cor_thd2 > 0.0 && cor_thd3 > 0.0 && cor_thd4 > 0.0)
	    apm->echo_cancellation()->set_cor_thds(cor_thd1, cor_thd2, cor_thd3, cor_thd4);

	// set log interval value
	int log_interval = 0;
	if(read_profile(profile_path, "AEC","set_log_interval", profile_buf) >= 0)
	    log_interval = atoi(profile_buf);
	if(log_interval > 0)
	    apm->echo_cancellation()->set_log_interval(log_interval);

	// set restrain parame
	int restrain_band_center=0;
	int restrain_band_wide=0;
	float restrain_factor=0.0;
	if(read_profile(profile_path, "AEC","set_restrain_band_center", profile_buf) >= 0)
	    restrain_band_center = atoi(profile_buf);
	if(read_profile(profile_path, "AEC","set_restrain_band_wide", profile_buf) >= 0)
	    restrain_band_wide = atoi(profile_buf);
	if(read_profile(profile_path, "AEC","set_restrain_factor", profile_buf) >= 0)
	    restrain_factor = atof(profile_buf);
	if(restrain_band_wide > 0 && restrain_band_center > restrain_band_wide && restrain_factor > 0.0f)
	    apm->echo_cancellation()->set_restrain_parameters(restrain_band_center, restrain_band_wide, restrain_factor);

	//set max suppression value
	if(read_profile(profile_path, "AEC","set_safe_suppression_value", profile_buf) >= 0) {
		float safe_suppression_value = 0.0f;
		safe_suppression_value = atof(profile_buf);
		if(safe_suppression_value > 0.0f){
			// printf("set safe_suppression_value %f \n",safe_suppression_value);
			apm->echo_cancellation()->set_aec_safe_suppression_value(safe_suppression_value);//1
		} else {
			printf("safe_suppression_value must > 0, but now is %f \n",safe_suppression_value);
		}
	}

	if(read_profile(profile_path, "AEC","enable_metrics", profile_buf) < 0) {
		apm->echo_cancellation()->enable_metrics(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->echo_cancellation()->enable_metrics(true);
	} else {
		apm->echo_cancellation()->enable_metrics(false);
	}

	if(read_profile(profile_path, "AEC","enable_delay_logging", profile_buf) < 0) {
		apm->echo_cancellation()->enable_delay_logging(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->echo_cancellation()->enable_delay_logging(true);
	} else {
		apm->echo_cancellation()->enable_delay_logging(false);
	}
#else
	apm->echo_cancellation()->Enable(true);
	apm->echo_cancellation()->enable_drift_compensation(false);
	apm->echo_cancellation()->set_suppression_level(EchoCancellation::kLowSuppression);
	//apm->echo_cancellation()->set_suppression_level(EchoCancellation::kModerateSuppression);
	//apm->echo_cancellation()->set_suppression_level(EchoCancellation::kHighSuppression);
	apm->echo_cancellation()->enable_metrics(true);
	apm->echo_cancellation()->enable_delay_logging(true);
#endif
#endif

#ifdef AECM
	if(!apm->echo_cancellation()->is_enabled()){
	  if(read_profile(profile_path, "AECM","enable_echo_control", profile_buf) < 0) {
	    apm->echo_control_mobile()->Enable(true);
	  } else if(!strcmp(profile_buf, "true")) {
	    apm->echo_control_mobile()->Enable(true);
	  } else {
	    apm->echo_control_mobile()->Enable(false);
	  }

	  if(read_profile(profile_path, "AECM","echo_control_mode", profile_buf) < 0) {
	    apm->echo_control_mobile()->set_routing_mode(EchoControlMobile::kLoudEarpiece);
	  } else if(!strcmp(profile_buf, "kQuietEarpieceOrHeadset")) {
	    apm->echo_control_mobile()->set_routing_mode(EchoControlMobile::kQuietEarpieceOrHeadset);
	  } else if(!strcmp(profile_buf, "kEarpiece")) {
	    apm->echo_control_mobile()->set_routing_mode(EchoControlMobile::kEarpiece);
	  } else if(!strcmp(profile_buf, "kLoudEarpiece")) {
	    apm->echo_control_mobile()->set_routing_mode(EchoControlMobile::kLoudEarpiece);
	  }else if(!strcmp(profile_buf, "kSpeakerphone")) {
	    apm->echo_control_mobile()->set_routing_mode(EchoControlMobile::kSpeakerphone);
	  } else {
	    apm->echo_control_mobile()->set_routing_mode(EchoControlMobile::kLoudSpeakerphone);
	  }

	  if(read_profile(profile_path, "AECM","enable_comfort_noise", profile_buf) < 0) {
	    apm->echo_control_mobile()->enable_comfort_noise(true);
	  } else if(!strcmp(profile_buf, "true")) {
	    apm->echo_control_mobile()->enable_comfort_noise(true);
	  } else {
	    apm->echo_control_mobile()->enable_comfort_noise(false);
	  }
	}
#endif//AECM
#ifdef NS
/*ns*/
#if CONFIG_FILE
	if(read_profile(profile_path, "NS","NS_enable", profile_buf) < 0) {
		apm->noise_suppression()->Enable(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->noise_suppression()->Enable(true);
	} else {
		apm->noise_suppression()->Enable(false);
	}

	if(read_profile(profile_path, "NS","set_level", profile_buf) < 0) {
		apm->noise_suppression()->set_level(NoiseSuppression::kHigh);		//2
	} else if(!strcmp(profile_buf, "kLow")) {
		apm->noise_suppression()->set_level(NoiseSuppression::kLow);		//0
	} else if(!strcmp(profile_buf, "kModerate")) {
		apm->noise_suppression()->set_level(NoiseSuppression::kModerate);	//1
	} else if(!strcmp(profile_buf, "kHigh")) {
		apm->noise_suppression()->set_level(NoiseSuppression::kHigh);		//2
	} else if(!strcmp(profile_buf, "kVeryHigh")) {
		apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh);	//3
	}
#else
	apm->noise_suppression()->Enable(true);
	//apm->noise_suppression()->set_level(NoiseSuppression::kLow);
	//apm->noise_suppression()->set_level(NoiseSuppression::kModerate);
	//apm->noise_suppression()->set_level(NoiseSuppression::kHigh);
	apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh);
#endif//CONFIG_FILE
#endif//NS
#ifdef AGC
/*AGC*/
#if CONFIG_FILE
	if(read_profile(profile_path, "AGC","AGC_enable", profile_buf) < 0) {
		apm->gain_control()->Enable(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->gain_control()->Enable(true);
	} else {
		apm->gain_control()->Enable(false);
	}

	if(read_profile(profile_path, "AGC","set_mode", profile_buf) < 0) {
		apm->gain_control()->set_mode(GainControl::kFixedDigital);

	} else if(!strcmp(profile_buf, "kFixedDigital")) {

		apm->gain_control()->set_mode(GainControl::kFixedDigital);
	} else if(!strcmp(profile_buf, "kAdaptiveAnalog")) {

		apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog);
	}else {

		apm->gain_control()->set_mode(GainControl::kAdaptiveDigital);
	}




	if(read_profile(profile_path, "AGC","set_target_level_dbfs", profile_buf) < 0) {
		apm->gain_control()->set_target_level_dbfs(3);			//Limited to [0, 31];
	} else {

		apm->gain_control()->set_target_level_dbfs(atoi(profile_buf));			//Limited to [0, 31];3, 20
	}

	if(read_profile(profile_path, "AGC","set_compression_gain_db", profile_buf) < 0) {
		apm->gain_control()->set_compression_gain_db(1);		//Limited to [0, 90];
	} else {

		apm->gain_control()->set_compression_gain_db(atoi(profile_buf));		//Limited to [0, 90];9, 30
	}

	if(read_profile(profile_path, "AGC","enable_limiter", profile_buf) < 0) {
		apm->gain_control()->enable_limiter(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->gain_control()->enable_limiter(true);
	} else {
		apm->gain_control()->enable_limiter(false);
	}
#else
	apm->gain_control()->Enable(true);
	apm->gain_control()->set_mode(GainControl::kFixedDigital);
	apm->gain_control()->set_target_level_dbfs(6);//[0, 31]or negative ???
	apm->gain_control()->set_compression_gain_db(30);//[0,90]
	apm->gain_control()->enable_limiter(false);//When an analog mode is set
	//apm->gain_control()->set_analog_level_limits(10);//[0,255]
	//apm->gain_control()->set_analog_level_limits(300,60000);//[0,65535]
#endif//CONFIG_FILE
#endif//AGC
#ifdef LE
/*LE*/
#if CONFIG_FILE
	if(read_profile(profile_path, "LE","LE_enable", profile_buf) < 0) {
		apm->level_estimator()->Enable(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->level_estimator()->Enable(true);
	} else {
		apm->level_estimator()->Enable(false);
	}
#else
	apm->level_estimator()->Enable(true);
#endif//CONFIG_FILE
#endif//LE
#ifdef VAD
/*vad*/
#if CONFIG_FILE
	if(read_profile(profile_path, "VAD","VAD_enable", profile_buf) < 0) {
		apm->voice_detection()->Enable(true);
	} else if(!strcmp(profile_buf, "true")) {
		apm->voice_detection()->Enable(true);
	} else {
		apm->voice_detection()->Enable(false);
	}

	if(read_profile(profile_path, "VAD","set_likelihood", profile_buf) < 0) {
		apm->voice_detection()->set_likelihood(VoiceDetection::kLowLikelihood);		//1
	} else if(!strcmp(profile_buf, "kVeryLowLikelihood")) {
		apm->voice_detection()->set_likelihood(VoiceDetection::kVeryLowLikelihood);	//0
	} else if(!strcmp(profile_buf, "kLowLikelihood")) {
		apm->voice_detection()->set_likelihood(VoiceDetection::kLowLikelihood);		//1
	} else if(!strcmp(profile_buf, "kModerateLikelihood")) {
		apm->voice_detection()->set_likelihood(VoiceDetection::kModerateLikelihood);	//2
	} else if(!strcmp(profile_buf, "kHighLikelihood")) {
		apm->voice_detection()->set_likelihood(VoiceDetection::kHighLikelihood);	//3
	}

	if(read_profile(profile_path, "VAD","set_frame_size_ms", profile_buf) < 0) {
		apm->voice_detection()->set_frame_size_ms(30);
	} else if(!strcmp(profile_buf, "10")) {
		apm->voice_detection()->set_frame_size_ms(10);
	} else if(!strcmp(profile_buf, "20")) {
		apm->voice_detection()->set_frame_size_ms(20);
	} else if(!strcmp(profile_buf, "30")) {
		apm->voice_detection()->set_frame_size_ms(30);
	}
#else
	apm->voice_detection()->Enable(true);
	//apm->voice_detection()->set_likelihood(VoiceDetection::kVeryLowLikelihood);
	//apm->voice_detection()->set_likelihood(VoiceDetection::kLowLikelihood);
	//apm->voice_detection()->set_likelihood(VoiceDetection::kModerateLikelihood);
	apm->voice_detection()->set_likelihood(VoiceDetection::kModerateLikelihood);
	apm->voice_detection()->set_frame_size_ms(10);
#endif//CONFIG_FILE
#endif//VAD

	if(read_profile(profile_path, "Set_Near_Frame","delay_ms", profile_buf) < 0) {
		delay_time = 150;
	} else {
		delay_time = atoi(profile_buf);
	}

	if(read_profile(profile_path, "Set_Far_Frame","Frame_V", profile_buf) < 0) {
		frame_far_v = 1.0;
	} else {
		frame_far_v = atof(profile_buf);
	}

	if(read_profile(profile_path, "Set_Near_Frame","Frame_V", profile_buf) < 0) {
		frame_near_v = 1.0;
	} else {
		frame_near_v = atof(profile_buf);
	}

	//dump();
	// apm->Initialize(input_sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz, input_layout, output_layout, reverse_layout);

	far_frame = new AudioFrame();
	if(far_frame == NULL) {
		printf("new far AudioFrame error\n");
		return -1;
	}

	near_frame = new AudioFrame();
	if(near_frame == NULL) {
		printf("new near AudioFrame error\n");
		return -1;
	}
/*
	printf("delay_time = %d\n",delay_time);
	printf("far_Frame_V = %f\n",frame_far_v);
	printf("near_Frame_V = %f\n",frame_near_v);
	dump();
*/
	return 0;
}


int ingenic_apm_set_far_frame(short *buf) {
	int i, ret;
	far_frame->num_channels_ = CHANNEL_NUM;
	far_frame->sample_rate_hz_ = SAMPLE_RATE;
	far_frame->samples_per_channel_ = far_frame->sample_rate_hz_ * SAMPLE_TIME / 1000;

	for(i=0;i<(int)far_frame->samples_per_channel_;i++) {
		far_frame->data_[i]=(short)((float)buf[i] * frame_far_v);
	}
#ifdef VAD
	//ret = apm->voice_detection()->stream_has_voice();
	//printf("voice_detection..: %d\n",ret);
#endif
	ret = apm->ProcessReverseStream(far_frame);
	if(ret < 0) {
		printf("ProcessReverseStream error : %d---\n", ret);
	}

	return 0;
}

int ingenic_apm_set_near_frame(short *input, short *output){
  //printf("ingenic_apm_set_near_frame..\n");
	int i, ret;
	near_frame->num_channels_ = CHANNEL_NUM;
	near_frame->sample_rate_hz_ = SAMPLE_RATE;
	near_frame->samples_per_channel_ = near_frame->sample_rate_hz_ * SAMPLE_TIME / 1000;

	for(i=0;i<(int)near_frame->samples_per_channel_;i++){
		near_frame->data_[i]=(short)((float)input[i] * frame_near_v);
	}
	//struct timeval currentTime;
	//gettimeofday(&currentTime, NULL);
	//printf("process time: %lld\n",currentTime.tv_sec*1000 + currentTime.tv_usec/1000);
	apm->set_stream_delay_ms(delay_time);
	//apm->set_stream_delay_ms(delaytime);

#ifdef VAD
	//apm->voice_detection()->set_stream_has_voice();
	//ret = apm->voice_detection()->stream_has_voice();
	//printf("voice_detection..: %d\n",ret);
#endif
	ret = apm->ProcessStream(near_frame);
	if(ret < 0) {
		printf("ProcessStream() error : %d\n", ret);
	}

#if 0//def VAD
	ret = apm->voice_detection()->stream_has_voice();
	//printf("voice_detection: %d\n",ret);
	if(ret == 0)
	  {
	    vadCount++;
	    //if(vadCount>5)
	      {
		/* there is not human voice */
		for(i = 0; i < (int)near_frame->samples_per_channel_; i++){
		  //near_frame->data_[i] = near_frame->data_[i] / 5;
		  near_frame->data_[i] = near_frame->data_[i];
		  //near_frame->data_[i] = 0;
	      }
	    }
	  }
	else
	  vadCount = 0;
#endif

	memcpy(output, near_frame->data_, near_frame->samples_per_channel_*sizeof(short));

	return 0;
}

void ingenic_apm_destroy() {
	//dump();
	if(apm != NULL){
		delete apm;//remove this method
		apm = NULL;
	}

	if (far_frame != NULL) {
		delete far_frame;
		far_frame = NULL;
	}

	if (near_frame != NULL) {
		delete near_frame;
		near_frame = NULL;
	}
}

